平心而论,你的大脑是个了不起的结构。
你出生了,睁开眼睛初见三维世界。
你能够辨认出爸爸、妈妈,渐渐长大,认识了周围这100来号老师同学。
你身心健康全面发展,一路过关斩将,升到了高中,同时应付8门或者9门功课。
你完成了这么许多了不起的任务,凭借什么呢?凭借你这1斤多的神经系统。在你这这1斤多神经里,有大概1000亿个神经元。
我们复习一下初中生物知识,大部分神经元长这个样子:
它有一个胞体,上面长着一些短枝丫,叫做树突。还会有一根长枝丫,叫做轴突。
神经元用树突收集来自上游神经元的信号,然后信号沿着胞体-轴突一路传导,最终传递给下游的神经元。
我们可以把神经元看做是有一定响应规则的电学元件。无论神经系统感受到什么信号:光信号、压力信号(声压和触压)、温度信号等等,都是先转化为电信号再在神经系统中传递的。
那么,神经元作为一个电子元件,它的“输入”(来自上游神经元的信号)与“输出”(来自下游神经元的信号)遵循什么规律呢?
我们先来研究一下在神经元中传递的信号——动作电位。
如果你用一个非常精细的电压表测量一个神经元细胞膜内外的电位差,你就能测量到这样一串的动作电位(Action potential)。需要注意的是,动作电位具有“全”或“无”的特点。也就是说,如果神经元的“输入”小于一个阈值,就无法产生动作电位,那么这个神经元也就没有输出。如果神经元的“输入”大于某个阈值,就可以激活一个动作电位,那么这个神经元就有一个输出。
这明显是有道理的:你的神经元并不打算传递微小的噪声信号,只打算传递超过一定阈值的确切的信号。
于是,在模拟单个神经元的“输入”与“输出”关系时,我们要考虑这个产生“全或无”阈值的函数,我们称之为“激活函数”。
请你思考一下:你有多少种方式可以实现这样一种阶跃函数呢?
如上所述,为了实现“全或无”的阈值功能,我们需要一个阶跃函数。让它在输入值较小的情况下,输出为0 。当输入值大于某个阈值的时候,立刻得到一个输出。
比起上面那个尖锐冷酷的阶跃函数,我们在计算上通常采用下面这种S形的函数。
S函数(sigmod函数)对制作神经网络非常重要!
它的公式为:
我知道它看上去很恐怖。然而,在计算中,你会体会到S函数的妙处:它足够简单。
相信我,科学家们决定使用某个公式,通常都是因为它计算起来简单,而不是复杂。
于是,现在我们可以模拟一个神经元的输入与输出了:
先计算多个输入的总和,再使用sigmod函数来感受一下这个输入是否超过了“阈值”,然后给出一个输出。
之前有提到过,在我们的大脑中有1000亿个神经元(这恰好是银河系中恒星的大致数量),这些神经元组成了一个复杂的网络。
现在我们将对这个网络进行模拟。构建多层神经元,每一层中的神经元都与在其前后层的神经元互相连接。就像下图这样:
你可以看到这是一个“全连接”网络,3层结构,每一层都有3个“神经元”,每个神经元都与之前一层和之后一层的每一个神经元相连接。
它和我们“真实”的神经网络之间有何差异呢?差异在于真实的神经网络中神经元虽然也分“层”,但单个的神经元和之前、之后一层的不同神经元之间是以不同的强度来进行连接的。
于是我们需要为人工神经元网络也设置一下节点之间的连接强度。
下图再一次显示了连接的神经元节点,但是这次在每个连接上显示了相关的权重。较小的权重将弱化信号,而较大的权重将放大信号。
解释一下下角标:权重w 2,3 是指与前一层节点2传递给下一层的节点3的信号的权重。
如此一来,我们便完成了一个最初的网络的构建。
而当我们训练这个“人工神经元网络”去学习一项任务的时候,我们训练的是什么呢?
其实就是在训练各层神经元之间连接的权重。
请你思考一下,如果最后训练出的神经元网络结构中,某个连接的权重等于0,它是什么意思呢?
我们已经学会了如何搭建一个以“人工神经元”为节点的网络。
现在我们可以给这个网络输入一些信号,来观察它的输出。
下面呢,我们用一个最最最简单的两层神经网络来练习一下如何在神经网络中追踪信号。
假设有上面这样一个网络,它只有2层4个神经元。之间的信号权重如图所示。
第一层作为我们的输入层。
我们给第一层的第1个神经元一个1.0的输入,给第一层的第二个神经元0.5的输入。(我们默认对于输入层的神经元,它的输出直接等于输入,是不经过S函数的处理)
我们来计算一下:
第二层的第1个神经元接收到的输入是什么?
第二层的第1个神经元在使用S函数处理接收到的信号之后,它给出的输出又是什么?
# 导入scipy,这是一个常用的包
import scipy.special
#第一层的2个神经元输入分别是1.0和0.5
inputsignal_11 = 1.0
inputsignal_12 = 0.5
#图中所给2层神经元连接权重如下
w11 = 0.9
w21 = 0.3
#调用scipy包,定义我们的S函数
s_function = lambda x : scipy.special.expit(x)
#计算第二层第1个神经元的输入
inputsignal_21 = w11*inputsignal_11 + w21*inputsignal_12
print(inputsignal_21)
#计算第二层第1个神经元的输出
outputsignal_21 = scipy.special.expit(inputsignal_21)
print(outputsignal_21)
现在我们已经学会了如何在神经网络中追踪信号!
你甚至可以用python语言来把代码写出来。
请你仿照上面的代码示例,写出新的代码,完成以下计算:
第二层的第2个神经元接收到的输入是什么?
第二层的第1个神经元在使用S函数处理接收到的信号之后,它给出的输出又是什么?
# 请你仿照上面的代码,完成以下计算:
# 第二层的第2个神经元接收到的输入是什么?
# 第二层的第1个神经元在使用S函数处理接收到的信号之后,它给出的输出又是什么?